
using System;
using System.Text;

internal class ByteBuffer{
	
	internal static readonly bool DEBUG = false;
	internal static readonly int BO_BIG_ENDIAN = 0;
	internal static readonly int BO_LITTLE_ENDIAN = 1;
	
	private int byteOrder = BO_BIG_ENDIAN;
	private short[] buf;
	private int size;
	private int curPos;
	private bool hasBeenErrors;
	private bool expandable=true;
	private int expandBy = 4096;
	
	
	internal ByteBuffer(int size, int byteOrdering){
		if(size < 1){
			size = 1;
		}
		buf = new short[size];
		this.size = size;
		this.byteOrder = byteOrdering;
		curPos = 0;
		hasBeenErrors = false;
	}
	
	
	internal ByteBuffer(byte[] content, int byteOrdering){
		try{
			buf = new short[content.Length];
			for(int i=0;i<content.Length;i++){
				buf[i] = (short)(content[i] & 255);
			}
			size = content.Length;
			this.byteOrder = byteOrdering;
			curPos = 0;
			hasBeenErrors = false;
		}catch(Exception e){
			//System.Console.WriteLine("ByteBuffer: Couldn't create buffer from empty array.");
		}
	}
	
	
	
	internal void setExpandable(bool exp){
		expandable = exp;
	}
	
	internal void setExpandBy(int expBy){
		
		if(expBy > 1024){
			this.expandBy = expBy;
		}
		
	}
	
	internal void setByteOrder(int byteOrder){
		
		if(byteOrder>=0 && byteOrder<2){
			this.byteOrder = byteOrder;
		}
		
	}

    internal byte[] getBytes(){
		byte[] ret = new byte[buf.Length];
		for(int i=0;i<buf.Length;i++){
			ret[i] = (byte)buf[i];
		}
		return ret;
	}
	
	internal int getSize(){
		return this.size;
	}
	
	internal int getPos(){
		return curPos;
	}
	
	private void error(){
		hasBeenErrors = true;
		//System.Console.WriteLine("Not in range!");
	}
	
	internal bool hasHadErrors(){
		return hasBeenErrors;
	}
	
	internal void clear(){
		for(int i=0;i<buf.Length;i++){
			buf[i] = 0;
		}
		curPos=0;
	}
	
	internal void fill(byte value){
		for(int i=0;i<size;i++){
			buf[i]=value;
		}
	}
	
	internal bool fillRange(int start, int length, byte value){
		if(inRange(start,length)){
			for(int i=start;i<(start+length);i++){
				buf[i] = value;
			}
			return true;
		}else{
			error();
			return false;
		}
	}
	
	internal void resize(int length){
		
		short[] newbuf = new short[length];
		System.Array.Copy(buf,0,newbuf,0,Math.Min(length,size));
		buf = newbuf;
		size = length;
		
	}
	
	internal void resizeToCurrentPos(){
		resize(curPos);
	}
	
	internal void expand(){
		expand(expandBy);
	}
	
	internal void expand(int byHowMuch){
		resize(size+byHowMuch);
	}
	
	internal void goTo(int position){
		if(inRange(position)){
			curPos = position;
		}else{
			error();
		}
	}
	
	internal void move(int howFar){
		curPos += howFar;
		if(!inRange(curPos)){
			curPos = size-1;
		}
	}
	
	internal bool inRange(int pos){
		if(pos >= 0 && pos < size){
			return true;
		}else{
			if(expandable){
				expand(Math.Max(pos+1-size,expandBy));
				return true;
			}else{
				return false;
			}
		}
	}
	
	internal bool inRange(int pos, int length){
		if(pos >= 0 && pos+(length-1) < size){
			return true;
		}else{
			if(expandable){
				expand(Math.Max(pos+length-size,expandBy));
				return true;
			}else{
				return false;
			}
		}
	}
	
	internal bool putBoolean(bool b){
		bool ret = putBoolean(b,curPos);
		move(1);
		return ret;
	}
	
	internal bool putBoolean(bool b, int pos){
		if(b){
			return putByte((short)1,pos);
		}else{
			return putByte((short)0,pos);
		}
	}
	
	internal bool putByte(short var){
		if(inRange(curPos,1)){
			buf[curPos] = var;
			move(1);
			return true;
		}else{
			error();
			return false;
		}
	}
	
	internal bool putByte(short var, int pos){
		if(inRange(pos,1)){
			buf[pos] = var;
			return true;
		}else{
			error();
			return false;
		}
	}
	
	internal bool putShort(short var){
		bool ret = putShort(var,curPos);
		if(ret){
			move(2);
		}
		return ret;
	}
		
	internal bool putShort(short var, int pos){
		if(inRange(pos,2)){
			if(this.byteOrder == BO_BIG_ENDIAN){
				buf[pos+0] = (short)((var>>8)&255);
				buf[pos+1] = (short)((var)&255);
			}else{
				buf[pos+1] = (short)((var>>8)&255);
				buf[pos+0] = (short)((var)&255);
			}
			return true;
		}else{
			error();
			return false;
		}
	}
	
	internal bool putInt(int var){
		bool ret = putInt(var,curPos);
		if(ret){
			move(4);
		}
		return ret;
	}
	
	internal bool putInt(int var, int pos){
		if(inRange(pos,4)){
			if(this.byteOrder == BO_BIG_ENDIAN){
				buf[pos+0] = (short)((var>>24)&255);
				buf[pos+1] = (short)((var>>16)&255);
				buf[pos+2] = (short)((var>> 8)&255);
				buf[pos+3] = (short)((var    )&255);
			}else{
				buf[pos+3] = (short)((var>>24)&255);
				buf[pos+2] = (short)((var>>16)&255);
				buf[pos+1] = (short)((var>> 8)&255);
				buf[pos+0] = (short)((var    )&255);
			}
			return true;
		}else{
			error();
			return false;
		}
	}
	
	internal bool putString(string var){
		bool ret = putString(var,curPos);
		if(ret){
			move(2*var.Length);
		}
		return ret;
	}
	
	internal bool putString(string var, int pos){
		char[] charArr = var.ToCharArray();
		short theChar;
		if(inRange(pos,var.Length*2)){
			for(int i=0;i<var.Length;i++){
				theChar = (short)(charArr[i]);
				buf[pos+0] = (short)((theChar>>8)&255);
				buf[pos+1] = (short)((theChar   )&255);
				pos+=2;
			}
			return true;
		}else{
			error();
			return false;
		}
	}
	
	internal bool putChar(char var){
		bool ret = putChar(var,curPos);
		if(ret){
			move(2);
		}
		return ret;
	}
	
	internal bool putChar(char var, int pos){
		int tmp = var;
		if(inRange(pos,2)){
			if(byteOrder == BO_BIG_ENDIAN){
				buf[pos+0] = (short)((tmp>>8)&255);
				buf[pos+1] = (short)((tmp   )&255);
			}else{
				buf[pos+1] = (short)((tmp>>8)&255);
				buf[pos+0] = (short)((tmp   )&255);
			}
			return true;
		}else{
			error();
			return false;
		}
	}
	
	internal bool putCharAscii(char var){
		bool ret = putCharAscii(var,curPos);
		if(ret){
			move(1);
		}
		return ret;
	}
	
	internal bool putCharAscii(char var, int pos){
		if(inRange(pos)){
			buf[pos] = (short)var;
			return true;
		}else{
			error();
			return false;
		}
	}
	
	internal bool putStringAscii(string var){
		bool ret = putStringAscii(var,curPos);
		if(ret){
			move(var.Length);
		}
		return ret;
	}
	
	internal bool putStringAscii(string var, int pos){
		char[] charArr = var.ToCharArray();
		if(inRange(pos,var.Length)){
			for(int i=0;i<var.Length;i++){
				buf[pos] = (short)charArr[i];
				pos++;
			}
			return true;
		}else{
			error();
			return false;
		}
	}
	
	internal bool putByteArray(short[] arr){
		if(arr==null)return false;
		if(buf.Length-curPos < arr.Length){
			resize(curPos+arr.Length);
		}
		for(int i=0;i<arr.Length;i++){
			buf[curPos+i] = (byte)arr[i];
		}
		curPos += arr.Length;
		return true;
	}
	
	internal bool readByteArray(short[] arr){
		if(arr==null)return false;
		if(buf.Length-curPos < arr.Length)return false;
		for(int i=0;i<arr.Length;i++){
			arr[i] = (short)(buf[curPos+i]&0xFF);
		}
		curPos += arr.Length;
		return true;
	}
	
	internal bool putShortArray(short[] arr){
		if(arr==null)return false;
		if(buf.Length-curPos < arr.Length*2){
			resize(curPos+arr.Length*2);
		}
		if(byteOrder == BO_BIG_ENDIAN){
			for(int i=0;i<arr.Length;i++){
				buf[curPos+0] = (short)((arr[i]>>8)&255);
				buf[curPos+1] = (short)((arr[i]   )&255);
				curPos+=2;
			}
		}else{
			for(int i=0;i<arr.Length;i++){
				buf[curPos+1] = (short)((arr[i]>>8)&255);
				buf[curPos+0] = (short)((arr[i]   )&255);
				curPos+=2;
			}
		}
		return true;
	}
	
	internal string toString(){
		StringBuilder strBuf = new StringBuilder();
		short tmp;
		for(int i=0;i<(size-1);i+=2){
			tmp = (short)((buf[i]<<8)|(buf[i+1]));
			strBuf.Append((char)(tmp));
		}
		return strBuf.ToString();
	}
	
	internal string toStringAscii(){
		StringBuilder strBuf = new StringBuilder();
		for(int i=0;i<size;i++){
			strBuf.Append((char)(buf[i]));
		}
		return strBuf.ToString();
	}
	
	internal bool readBoolean(){
		bool ret = readBoolean(curPos);
		move(1);
		return ret;
	}
	
	internal bool readBoolean(int pos){
		return readByte(pos)==1;
	}
	
	internal short readByte()
    {
		short ret = readByte(curPos);
		move(1);
		return ret;
	}
	
	internal short readByte(int pos)
    {
		if(inRange(pos)){
			return buf[pos];
		}else{
			error();
			throw new IndexOutOfRangeException();
		}
	}
	
	internal short readShort()
    {
		short ret = readShort(curPos);
		move(2);
		return ret;
	}
	
	internal short readShort(int pos)
    {
		if(inRange(pos,2)){
			if(this.byteOrder == BO_BIG_ENDIAN){
				return (short)((buf[pos]<<8)|(buf[pos+1]));
			}else{
				return (short)((buf[pos+1]<<8)|(buf[pos]));
			}
		}else{
			error();
			throw new IndexOutOfRangeException();
		}
	}
	
	internal int readInt()
    {
		int ret = readInt(curPos);
		move(4);
		return ret;
	}
	
	internal int readInt(int pos)
    {
		int ret=0;
		if(inRange(pos,4)){
			if(this.byteOrder == BO_BIG_ENDIAN){
				ret |= (buf[pos+0]<<24);
				ret |= (buf[pos+1]<<16);
				ret |= (buf[pos+2]<< 8);
				ret |= (buf[pos+3]    );
			}else{
				ret |= (buf[pos+3]<<24);
				ret |= (buf[pos+2]<<16);
				ret |= (buf[pos+1]<< 8);
				ret |= (buf[pos+0]    );
			}
			return ret;
		}else{
			error();
			throw new IndexOutOfRangeException();
		}
	}
	
	internal char readChar()
    {
		char ret = readChar(curPos);
		move(2);
		return ret;
	}
	
	internal char readChar(int pos)
    {
		if(inRange(pos,2)){
			return (char)(readShort(pos));
		}else{
			error();
			throw new IndexOutOfRangeException();
		}
	}
	
	internal char readCharAscii()
    {
		char ret = readCharAscii(curPos);
		move(1);
		return ret;
	}
	
	internal char readCharAscii(int pos)
    {
		if(inRange(pos,1)){
			return (char)(readByte(pos)&255);
		}else{
			error();
			throw new IndexOutOfRangeException();
		}
	}
	
	internal string readString(int length)
    {
		if(length > 0){
			string ret = readString(curPos,length);
			move(ret.Length*2);
			return ret;
		}else{
			return "";
		}
	}
	
	internal string readString(int pos, int length)
    {
		char[] tmp;
		if(inRange(pos,length*2) && length>0){
			 tmp = new char[length];
			 for(int i=0;i<length;i++){
			 	tmp[i] = readChar(pos+i*2);
			 }
			 return new String(tmp);
		}else{
			throw new IndexOutOfRangeException();
		}
	}
	
	internal string readStringWithShortLength()
    {
		string ret = readStringWithShortLength(curPos);
		move(ret.Length*2+2);
		return ret;
	}
	
	internal string readStringWithShortLength(int pos){
		short len;
		if(inRange(pos,2)){
			len = readShort(pos);
			if(len>0){
				return readString(pos+2,len);
			}else{
				return "";
			}
		}else{
			throw new IndexOutOfRangeException();
		}
	}
	
	internal string readStringAscii(int length){
		string ret = readStringAscii(curPos,length);
		move(ret.Length);
		return ret;
	}
	
	internal string readStringAscii(int pos, int length){
		char[] tmp;
		if(inRange(pos,length) && length > 0){
			tmp = new char[length];
			for(int i=0;i<length;i++){
				tmp[i] = readCharAscii(pos+i);
			}
			return new String(tmp);
		}else{
			throw new IndexOutOfRangeException();
		}
	}
	
	internal string readStringAsciiWithShortLength(){
		string ret = readStringAsciiWithShortLength(curPos);
		move(ret.Length+2);
		return ret;
	}
	
	internal string readStringAsciiWithShortLength(int pos){
		short len;
		if(inRange(pos,2)){
			len = readShort(pos);
			if(len > 0){
				return readStringAscii(pos+2,len);
			}else{
				return "";
			}
		}else{
            throw new IndexOutOfRangeException();
		}
	}
	
	private short[] expandShortArray(short[] array, int size){
		short[] newArr = new short[array.Length+size];
		if(size>0){
			System.Array.Copy(array,0,newArr,0,array.Length);
		}else{
			System.Array.Copy(array,0,newArr,0,newArr.Length);
		}
		return newArr;
	}
	

    internal short[] GetBuffer()
    {
        crop();
        return buf;
    }

	internal void crop(){
		if(curPos>0){
			if(curPos<buf.Length){
				short[] newBuf = new short[curPos];
				System.Array.Copy(buf,0,newBuf,0,curPos);
				buf = newBuf;
			}
		}else{
			//System.Console.WriteLine("Could not crop buffer, as the current position is 0. The buffer may not be empty.");
		}
	}
	
	internal static ByteBuffer asciiEncode(ByteBuffer buf){
		
		short[] data = buf.buf;
		byte[] enc = new byte[buf.getSize()*2];
		
		int encpos = 0;
		int tmp;
		for(int i=0;i<data.Length;i++){
			
			tmp = data[i];
			enc[encpos  ] = (byte)(65+(tmp   ) & 0xF);
			enc[encpos+1] = (byte)(65+(tmp>>4) & 0xF);
			encpos += 2;
			
		}
		return new ByteBuffer(enc,ByteBuffer.BO_BIG_ENDIAN);
		
	}
	
	internal static ByteBuffer asciiDecode(ByteBuffer buf){
		return null;
	}
	
	
	
	
}